home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / shells / scsh-0.4 / scsh-0 / scsh-0.4.2 / rts / bignum.scm < prev    next >
Text File  |  1995-10-13  |  10KB  |  337 lines

  1. ; Copyright (c) 1993, 1994 Richard Kelsey and Jonathan Rees.  See file COPYING.
  2.  
  3. ; This is file bignum.scm.
  4.  
  5. ; Integer arithmetic
  6.  
  7. (define-extended-number-type :bignum (:exact-integer)
  8.   (make-bignum sign magnitude)
  9.   bignum?
  10.   (sign bignum-sign)
  11.   (magnitude bignum-magnitude))
  12.  
  13. (define (integer->bignum m)
  14.   (if (bignum? m)
  15.       m
  16.       (cond ((>= m 0)
  17.          (make-bignum 1 (integer->magnitude m)))
  18.         ((= m least-non-bignum)
  19.          (make-bignum -1 least-non-bignum-magnitude))
  20.         (else
  21.          (make-bignum -1 (integer->magnitude (- 0 m)))))))
  22.  
  23. ;(define (bignum->integer n)             ;For debugging
  24. ;  (* (bignum-sign n)
  25. ;     (reduce (lambda (d n) (+ d (* n radix)))
  26. ;             0
  27. ;             (bignum-magnitude n))))
  28.  
  29. (define (make-integer sign mag)
  30.   (if (> sign 0)
  31.       (if (smaller-magnitude? greatest-non-bignum-magnitude mag)
  32.       (make-bignum sign mag)
  33.       (magnitude->integer mag))
  34.       (if (smaller-magnitude? least-non-bignum-magnitude mag)
  35.       (make-bignum sign mag)
  36.       (if (same-magnitude? mag least-non-bignum-magnitude)
  37.           least-non-bignum
  38.           (- 0 (magnitude->integer mag))))))
  39.  
  40. ; Arithmetic
  41.  
  42. (define (integer+ m n)
  43.   (let ((m (integer->bignum m))
  44.     (n (integer->bignum n)))
  45.     (let ((m-sign (bignum-sign m))
  46.       (m-mag (bignum-magnitude m))
  47.       (n-sign (bignum-sign n))
  48.       (n-mag (bignum-magnitude n)))
  49.     (if (= m-sign n-sign)
  50.     (make-integer m-sign (add-magnitudes m-mag n-mag))
  51.     (if (smaller-magnitude? m-mag n-mag)
  52.         (make-integer (- 0 m-sign) (subtract-magnitudes n-mag m-mag))
  53.         (make-integer m-sign (subtract-magnitudes m-mag n-mag)))))))
  54.  
  55. (define (integer- m n)
  56.   (integer+ m (integer-negate n)))
  57.  
  58. (define (integer-negate m)
  59.   (cond ((bignum? m)
  60.      (make-integer (- 0 (bignum-sign m))
  61.                (bignum-magnitude m)))
  62.     ((= m least-non-bignum)
  63.      (make-bignum 1 least-non-bignum-magnitude))
  64.     (else (- 0 m))))
  65.  
  66. (define (integer* m n)
  67.   (let ((m (integer->bignum m))
  68.     (n (integer->bignum n)))
  69.     (make-integer (* (bignum-sign m) (bignum-sign n))
  70.           (multiply-magnitudes
  71.            (bignum-magnitude m)
  72.            (bignum-magnitude n)))))
  73.  
  74. (define (integer-divide m n cont)
  75.   (let ((m (integer->bignum m))
  76.     (n (integer->bignum n)))
  77.     (divide-magnitudes
  78.        (bignum-magnitude m)
  79.        (bignum-magnitude n)
  80.        (lambda (q r)
  81.      (cont (make-integer (* (bignum-sign m) (bignum-sign n)) q)
  82.            (make-integer (bignum-sign m) r))))))
  83.  
  84. (define (integer-quotient m n)
  85.   (integer-divide m n (lambda (q r) q)))
  86.  
  87. (define (integer-remainder m n)
  88.   (integer-divide m n (lambda (q r) r)))
  89.  
  90. (define integer=
  91.   (lambda (m n)
  92.     (let ((m (integer->bignum m))
  93.       (n (integer->bignum n)))
  94.       (and (= (bignum-sign m) (bignum-sign n))
  95.        (same-magnitude? (bignum-magnitude m)
  96.                 (bignum-magnitude n))))))
  97.  
  98. (define integer<
  99.   (lambda (m n)
  100.     (let ((m (integer->bignum m))
  101.       (n (integer->bignum n)))
  102.       (let ((m-sign (bignum-sign m))
  103.         (n-sign (bignum-sign n)))
  104.     (or (< m-sign n-sign)
  105.         (and (= m-sign n-sign)
  106.          (if (< m-sign 0)
  107.              (smaller-magnitude? (bignum-magnitude n)
  108.                      (bignum-magnitude m))
  109.              (smaller-magnitude? (bignum-magnitude m)
  110.                      (bignum-magnitude n)))))))))
  111.  
  112.  
  113. ; Magnitude (unsigned integer) arithmetic
  114.  
  115. (define log-radix 14)            ;Cutting it close here...
  116. (define radix (expt 2 log-radix))
  117.  
  118. (define greatest-non-bignum (+ (expt 2 28) (- (expt 2 28) 1)))
  119. (define least-non-bignum (* (expt 2 28) -2))
  120.  
  121. (define zero-magnitude '())
  122. (define zero-magnitude? null?)
  123.  
  124. (define (low-digit m)
  125.   (if (zero-magnitude? m)
  126.       0
  127.       (car m)))
  128.  
  129. (define (high-digits m)
  130.   (if (zero-magnitude? m)
  131.       m
  132.       (cdr m)))
  133.  
  134. (define (adjoin-digit d m)
  135.   (if (and (= d 0) (zero-magnitude? m))
  136.       m
  137.       (cons d m)))
  138.  
  139. (define (integer->magnitude n)
  140.   (if (= n 0)
  141.       zero-magnitude
  142.       (let ((digit (remainder n radix)))
  143.     (adjoin-digit digit
  144.               (integer->magnitude (quotient n radix))))))
  145.  
  146. (define (magnitude->integer m)
  147.   (if (zero-magnitude? m)
  148.       0
  149.       (+ (low-digit m)
  150.      (* radix (magnitude->integer (high-digits m))))))
  151.  
  152. (define greatest-non-bignum-magnitude
  153.   (integer->magnitude greatest-non-bignum))
  154.  
  155. (define least-non-bignum-magnitude
  156.   (adjoin-digit (- 0 (remainder least-non-bignum radix))
  157.         (integer->magnitude
  158.          (- 0 (quotient least-non-bignum radix)))))
  159.  
  160. ; Combine two numbers digitwise using op.
  161.  
  162. (define (combine-magnitudes m n op)
  163.   (let recur ((m m) (n n) (carry 0))
  164.     (if (and (zero-magnitude? m) (zero-magnitude? n))
  165.     (integer->magnitude carry)
  166.     (let ((result (+ carry (op (low-digit m) (low-digit n)))))
  167.       (let ((q (quotient result radix))
  168.         (r (remainder result radix)))
  169.         (if (< r 0)
  170.         (adjoin-digit (+ r radix)
  171.                   (recur (high-digits m)
  172.                      (high-digits n)
  173.                      (- q 1)))
  174.         (adjoin-digit r
  175.                   (recur (high-digits m)
  176.                      (high-digits n)
  177.                      q))))))))
  178.  
  179. (define (add-magnitudes m n)
  180.   (combine-magnitudes m n +))
  181.  
  182. (define (subtract-magnitudes m n)
  183.   (combine-magnitudes m n -))
  184.  
  185. ; Compare
  186.  
  187. (define same-magnitude? equal?)
  188.  
  189. (define (smaller-magnitude? m n)
  190.   (let loop ((m m) (n n) (a #f))
  191.     (cond ((zero-magnitude? m)
  192.        (or (not (zero-magnitude? n)) a))
  193.       ((zero-magnitude? n) #f)
  194.       (else
  195.        (loop (high-digits m)
  196.          (high-digits n)
  197.          (or (< (low-digit m) (low-digit n))
  198.              (and (= (low-digit m) (low-digit n)) a)))))))
  199.  
  200. ; Multiply
  201.  
  202. (define (multiply-magnitudes m n)
  203.   (let recur ((m m) (a zero-magnitude))
  204.     (if (zero-magnitude? m)
  205.     a
  206.     (let ((a (combine-magnitudes a n (lambda (d e)
  207.                        (+ d (* e (low-digit m)))))))
  208.       (adjoin-digit (low-digit a)
  209.             (recur (high-digits m) (high-digits a)))))))
  210.  
  211. ; Divide m/n: find q and r such that m = q*n + r, where 0 <= r < n.
  212. ; Oh no... time to get out Knuth...
  213. ; The main thing we don't do that Knuth does is to normalize the
  214. ; divisor (n) by shifting it left.
  215.  
  216. (define (divide-magnitudes m n cont)
  217.   (if (zero-magnitude? (high-digits n))
  218.       (divide-by-digit m (low-digit n)
  219.                (lambda (q r)
  220.              (cont q (adjoin-digit r zero-magnitude))))
  221.       (let recur ((m m) (cont cont))
  222.     (if (smaller-magnitude? m n)
  223.         (cont zero-magnitude m)
  224.         (recur
  225.          (high-digits m)
  226.          (lambda (q r)
  227.            ;; 0 <= r < n  and  d < b
  228.            ;; so  b*r + d < b*n.
  229.            (divide-step (adjoin-digit (low-digit m) r)
  230.                 n
  231.                 (lambda (q1 r)
  232.                   (cont (adjoin-digit q1 q) r)))))))))
  233.     
  234. ; Divide m by n, where  n <= m < b*n, i.e. 1 <= quotient < b.
  235. ; E.g.  if  n = 100  then  100 <= m <= 999
  236. ;       if  n = 999  then  999 <= m <= 9989
  237.  
  238. (define (divide-step m n cont)
  239.   (do ((m-high m (high-digits m-high))
  240.        (n-high n (high-digits n-high)))
  241.       ((zero-magnitude? (high-digits (high-digits n-high)))
  242.        ;; Occasionally q^ is one larger than the actual first digit.
  243.        ;; This loop will never iterate more than once.
  244.        (let loop ((q^ (min (guess-quotient-digit m-high n-high)
  245.                (- radix 1))))
  246.      (let ((r (combine-magnitudes m n (lambda (d e)
  247.                         (- d (* e q^))))))
  248.        (if (improper-magnitude? r)
  249.            ;; (begin (write `(addback ,m ,n ,q^ ,r)) (newline) ...)
  250.            (loop (- q^ 1))
  251.            (cont q^ r)))))))
  252.  
  253. ; Compute q such that [m1 m2 m3] = q*[n1 n2] + r with 0 <= r < [n1 n2]
  254. ; Can assume b <= [0 n1 n2] <= [m1 m2 m3] <= [n1 n2 b-1]
  255. ; Some examples:
  256. ;  m / n :  100[1] / 10[02], 099 / 10, 099[1] / 99[0], 999[8] / 99[99]
  257. ; Various hacks are possible to improve performance.  In particular, the
  258. ; second division can be eliminated if the divisor is normalized.
  259. ; See Knuth.
  260. ;  [m1 m2] = q0*[n1] + r0
  261. ;  [m1 m2 m3] = q0*[n1 n2] + r^
  262. ;  r^ = b*r0 + m3 - q0*n2
  263.  
  264. (define (guess-quotient-digit m n)
  265.   (let ((n1 (low-digit (high-digits n)))
  266.     (n2 (low-digit n))
  267.     (m1 (low-digit (high-digits (high-digits m))))
  268.     (m2 (low-digit (high-digits m)))
  269.     (m3 (low-digit m)))
  270.     (let ((m12 (+ (* m1 radix) m2)))
  271.       (let ((q0 (quotient m12 n1))
  272.         (r0 (remainder m12 n1)))
  273.     (let ((r^ (- (+ (* radix r0) m3) (* q0 n2)))
  274.           (n12 (+ (* n1 radix) n2)))
  275.       (let ((q1 (quotient r^ n12))
  276.         (r1 (remainder r^ n12)))
  277.         (if (> q1 0)
  278.         (begin (display "This should never happen: q1 = ")
  279.                (write q1) (newline)))
  280.         (let ((q (+ q0 q1)))
  281.           (if (< r1 0) (- q 1) q))))))))
  282.  
  283. (define (improper-magnitude? m)
  284.   (and (not (zero-magnitude? m))
  285.        (or (< (low-digit m) 0)
  286.        (improper-magnitude? (high-digits m)))))
  287.  
  288. ; Special case of division algorithm for single-digit divisor.
  289.  
  290. (define (divide-by-digit m d cont)
  291.   (if (= d 0)
  292.       (error "integer division by zero" m d)
  293.       (let recur ((m m) (cont cont))
  294.     (if (and (zero-magnitude? (high-digits m))
  295.          (< (low-digit m) d))
  296.         (cont zero-magnitude (low-digit m))
  297.         (recur (high-digits m)
  298.            (lambda (q r)
  299.              (let ((m1 (+ (low-digit m) (* radix r))))
  300.                (cont (adjoin-digit (quotient m1 d) q)
  301.                  (remainder m1 d)))))))))
  302.  
  303. ;(define (divide-test seed)
  304. ;  (let ((random (make-random seed)))
  305. ;    (let loop ()
  306. ;      (let* ((z1 (integer+ (random) (integer* (random) 10000000)))
  307. ;             (z2 (integer+ (random) (integer* (random) 10000000)))
  308. ;             (n (max z1 z2))
  309. ;             (r (min z1 z2))
  310. ;             (q (random))
  311. ;             (m (integer+ (integer* n q) r)))
  312. ;        (if (not (= n r))
  313. ;            (integer-divide m n
  314. ;                            (lambda (q1 r1)
  315. ;                              (if (and (= q q1) (= r r1))
  316. ;                                  (begin (display ".")
  317. ;                                         (force-output (current-output-port)))
  318. ;                                  (error "division error" m n q q1 r r1)))))
  319. ;        (loop)))))
  320.  
  321.  
  322. ; Extend the generic arithmetic operators.
  323.  
  324. (define-method &integer? ((n :bignum)) #t)
  325. (define-method &exact? ((n :bignum)) #t)
  326.  
  327. (define-method &+ ((n1 :exact-integer) (n2 :exact-integer)) (integer+ n1 n2))
  328. (define-method &- ((n1 :exact-integer) (n2 :exact-integer)) (integer- n1 n2))
  329. (define-method &* ((n1 :exact-integer) (n2 :exact-integer)) (integer* n1 n2))
  330. (define-method &= ((n1 :exact-integer) (n2 :exact-integer)) (integer= n1 n2))
  331. (define-method &< ((n1 :exact-integer) (n2 :exact-integer)) (integer< n1 n2))
  332.  
  333. (define-method "ient ((n1 :exact-integer) (n2 :exact-integer))
  334.   (integer-quotient n1 n2))
  335. (define-method &remainder ((n1 :exact-integer) (n2 :exact-integer))
  336.   (integer-remainder n1 n2))
  337.